package com.example.front.arrange;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * TaskConfigPostProcessor
 *
 * @author liming2
 * @date 2023/10/11 10:45
 */
@Slf4j
class TaskConfigProcessor implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
        AnnotationAttributes enableAttributes =
                AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableTaskArrange.class.getName()));
        Class<?>[] taskContexts;
        if (CollectionUtils.isEmpty(enableAttributes) || (taskContexts = enableAttributes.getClassArray("taskContexts")).length == 0) {
            return;
        }
        Map<Class<? extends Context>, Map<String, Config>> arrangeConfig = new HashMap<>();


        for (Class<?> contextClass : taskContexts) {
            String packageName = contextClass.getPackage().getName();
            Map<String, Config> configMap = new HashMap<>();
            arrangeConfig.put((Class<? extends Context>) contextClass, configMap);

            String[] beanNames = ((DefaultListableBeanFactory) registry).getBeanNamesForType(AbstractTask.class);
            for (String beanName : beanNames) {
                ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) registry.getBeanDefinition(beanName);
                Map<String, Object> annotationAttributesMap = beanDefinition.getMetadata().getAnnotationAttributes(TaskDependent.class.getName());
                AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(annotationAttributesMap);
                String taskName = beanDefinition.getBeanClassName();
                if (Objects.nonNull(taskName) && taskName.startsWith(packageName)) {
                    this.setConfig(taskName, configMap, annotationAttributes);
                }
            }
        }
        Map<Class<? extends Context>, List<Config>> arrangeConfigMap =
                arrangeConfig.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue().values())));

        // check 循环依赖
        for (Map.Entry<Class<? extends Context>, List<Config>> entry : arrangeConfigMap.entrySet()) {
            List<Config> configList = entry.getValue();
            for (Config config : configList) {
                List<Config> parentList = new ArrayList<>();
                for (Integer parentId : config.getParentIdList()) {
                    configList.stream().filter(c -> parentId.intValue() == c.getId()).findFirst().ifPresent(parentList::add);
                }
                config.setParentConfigList(parentList);
            }
            for (Config config : configList) {
                for (Config parent : config.getParentConfigList()) {
                    if (parent.getParentConfigList().stream().anyMatch(p -> config == p)) {
                        log.error("{} 和 {} 循环依赖", config.getTaskName(), parent.getTaskName());
                        throw new RuntimeException("任务配置错误");
                    }
                }
            }
        }
        //TaskFactory.setArrangeConfig(arrangeConfigMap);
    }

    private void setConfig(String taskName, Map<String, Config> configMap, AnnotationAttributes annotationAttributes) {
        Config config;
        if (configMap.containsKey(taskName)) {
            config = configMap.get(taskName);
        } else {
            config = new Config();
            config.setId(configMap.size() + 1);
            config.setTaskName(taskName);
            configMap.put(taskName, config);
        }
        Class<?>[] dependencies;
        if (!CollectionUtils.isEmpty(annotationAttributes) &&
                (dependencies = annotationAttributes.getClassArray("dependencies")).length > 0) {
            List<Integer> parentIdList = new ArrayList<>();
            for (Class<?> dependent : dependencies) {
                String dependTaskName = dependent.getName();
                if (configMap.containsKey(dependTaskName)) {
                    parentIdList.add(configMap.get(dependTaskName).getId());
                } else {
                    Config parentConfig = new Config();
                    parentConfig.setId(configMap.size() + 1);
                    parentConfig.setTaskName(dependTaskName);
                    configMap.put(dependTaskName, parentConfig);
                    parentIdList.add(parentConfig.getId());
                }
            }
            config.setParentIdList(parentIdList);
        } else {
            config.setParentIdList(Collections.singletonList(-1));
        }
    }
}
